package com.redhat.redwork.validation;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.xwork.StringEscapeUtils;
import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.ValidationAware;

/**
 * 以JSON格式写响应
 * @author Justin
 *
 */
public class ValidationResponseWriter {
	
//	private static final String VALIDATE_ONLY_PARAM = "struts.validateOnly";
	private static final String VALIDATE_JSON_FORMAT = "struts.enableJSONValidation";

	/**
	 * 
	 * @param actionSupport
	 * @param status 状态码（可选），为null则不设置
	 * @return struts2 返回字符串
	 * @throws Exception
	 */
	public String writeResponse(ActionSupport actionSupport, Integer status) throws Exception{
		
		HttpServletRequest request = ServletActionContext.getRequest();
		HttpServletResponse response = ServletActionContext.getResponse();
		
		// 有错且接受json格式
        if (actionSupport.hasErrors() && isJSON()) {
        	// 设置响应返回状态
            if (status != null && status >= 0){
                response.setStatus(status);
            }
            response.setCharacterEncoding("UTF-8");
            response.getWriter().print(buildResponse(actionSupport));
            response.setContentType("application/json");
            return Action.NONE;
        }
        
        // 无错且只校验（也是json）
//        String validateOnly = request.getParameter(VALIDATE_ONLY_PARAM);
//        if (validateOnly != null && "true".equals(validateOnly)) {
//            //there were no errors
//            response.setCharacterEncoding("UTF-8");
//            response.getWriter().print("/* {} */");
//            response.setContentType("application/json");
//            return Action.NONE;
//        }
        
        // 返回页面（不是json）
        //getReferer();
        return actionSupport.input();
	}
	
	// 是否Ajax验证
	protected boolean isJSON() {
		HttpServletRequest request = ServletActionContext.getRequest();
		
		// 请求头Accept为json
		String accept = request.getHeader("Accept");
		boolean acceptJson = accept != null && accept.indexOf("application/json") != -1;
		
		// 请求参数指定为json验证
		String jsonFormatStr = request.getParameter(VALIDATE_JSON_FORMAT);
		boolean jsonFormat = jsonFormatStr != null && "true".equals(jsonFormatStr);
		
		return acceptJson || jsonFormat;
	}
	
    /**
     * @return JSON string that contains the errors and field errors
     */
    @SuppressWarnings("unchecked")
    protected String buildResponse(ValidationAware validationAware) {
        //should we use FreeMarker here?
        StringBuilder sb = new StringBuilder();
        sb.append("/* { ");

        if (validationAware.hasErrors()) {
            //action errors
            if (validationAware.hasActionErrors()) {
                sb.append("\"errors\":");
                sb.append(buildArray(validationAware.getActionErrors()));
            }

            //field errors
            if (validationAware.hasFieldErrors()) {
                if (validationAware.hasActionErrors())
                    sb.append(",");
                sb.append("\"fieldErrors\": {");
                Map<String, List<String>> fieldErrors = validationAware
                    .getFieldErrors();
                for (Map.Entry<String, List<String>> fieldError : fieldErrors
                    .entrySet()) {
                    sb.append("\"");
                    //if it is model driven, remove "model." see WW-2721
                    sb.append(validationAware instanceof ModelDriven ? fieldError.getKey().substring(6)
                            : fieldError.getKey());
                    sb.append("\":");
                    sb.append(buildArray(fieldError.getValue()));
                    sb.append(",");
                }
                //remove trailing comma, IE creates an empty object, duh
                sb.deleteCharAt(sb.length() - 1);
                sb.append("}");
            }
        }

        sb.append("} */");
        /*response should be something like:
         * {
         *      "errors": ["this", "that"],
         *      "fieldErrors": {
         *            field1: "this",
         *            field2: "that"
         *      }
         * }
         */
        return sb.toString();
    }

    private String buildArray(Collection<String> values) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (String value : values) {
            sb.append("\"");
            sb.append(StringEscapeUtils.escapeJavaScript(value));
            sb.append("\",");
        }
        if (values.size() > 0)
            sb.deleteCharAt(sb.length() - 1);
        sb.append("]");
        return sb.toString();
    }
}
